/*
 * File:   calculator.cpp
 * Author: ScaV
 *
 * Created on November 26, 2011, 3:33 PM
 */

#include "calculator.h"
#include <cstdlib>
#include <string>
#include <cstring>

calculator::calculator() {
}

calculator::calculator(const calculator& orig) {
}

calculator::~calculator() {
	reset();
}

int calculator::setExp(const char *str) {
	std::string digit;
	char ch, *ps;
	bool isbreak = false;
	for (int i = 0; !isbreak; ++i) {
		ch = str[i];
		if (isdigit(ch)) {
			digit += ch;
			continue;
		} else if (!digit.empty()) {
			ps = new char[digit.length()];
			strcpy(ps, digit.c_str());
			out.push(ps);
			digit.clear();
		}
		if (ch == '!') {
			ps = new char[2];
			strcpy(ps, "0");
			out.push(ps);
		}
		if (ch == '-' && (str[i-1] == '(' || i == 0)) {
			ps = new char[2];
			strcpy(ps, "0");
			out.push(ps);
		}
		switch (ch) {
			case '(':
				ps = new char[1];
				ps[0] = '(';
				stc.push(ps);
				break;
			case ')':
				while (1) {
					ps = stc.top();
					stc.pop();
					if (ps[0] == '(')
						break;
					out.push(ps);
				}
				break;
			case ' ':
				break;
			case '\0':
				isbreak = true;
				break;
			default:
				while (!stc.empty() && getPriority(ch) <= getPriority(stc.top()[0])) {
					out.push(stc.top());
					stc.pop();
				}
				ps = new char[1];
				ps[0] = ch;
				stc.push(ps);
		}
	}
	while (!stc.empty()) {
		out.push(stc.top());
		stc.pop();
	}
	std::cout << "TRANSLATION READY" << std::endl;
	return 1;
}

PRIOR calculator::getPriority(char ch) {
	switch (ch) {
		case '-':	return PRIOR_MINUS;
		break;
		case '+':	return PRIOR_PLUS;
		break;
		case '*':	return PRIOR_PRODUCT;
		break;
		case '/':	return PRIOR_DIVIDE;
		break;
		case '^':	return PRIOR_POWER;
		break;
		case '!':	return PRIOR_FACTORIAL;
		break;
	}
	return PRIOR_UNKNOWN;
}

infint calculator::calc(const char *str) {
	infint *tmp1, *tmp2, *tmp3, res;
	char ch;
	reset();
	if (str != NULL)
		setExp(str);
	if (out.empty())
		return res;
	for (int i = 0; i < out.size(); ++i) {
		ch = out.front()[0];
		if (isdigit(ch)) {
			tmp1 = new infint(out.front());
			cstc.push(tmp1);
		} else {
			if (cstc.size() < 2)
				throw "Invalid expression!";
			tmp2 = cstc.top();
			cstc.pop();
			tmp1 = cstc.top();
			cstc.pop();
			tmp3 = new infint;
			switch (ch) {
				case '-':
					*tmp3 = (*tmp1) - (*tmp2);
					break;
				case '+':
					*tmp3 = (*tmp1) + (*tmp2);
					break;
				case '*':
					*tmp3 = (*tmp1) * (*tmp2);
					break;
				case '/':
					*tmp3 = (*tmp1) / (*tmp2);
					break;
				case '^':
					*tmp3 = (*tmp1) ^ (*tmp2);
					break;
				case '!':
					*tmp3 = tmp1->getFact();
					break;
			}
			cstc.push(tmp3);
			delete tmp1;
			delete tmp2;
		}
		out.push(out.front());
		out.pop();
	}
	res = *cstc.top();
	delete cstc.top();
	cstc.pop();
	return res;
}

void calculator::reset() {
	while (!out.empty()) {
		delete[] out.front();
		out.pop();
	}
	while (!stc.empty()) {
		delete[] stc.top();
		stc.pop();
	}
	while (!cstc.empty()) {
		delete cstc.top();
		cstc.pop();
	}
	return;
}